
#include <QDebug>
#include <cmath>

#include "./axis.hpp"
#include "spdlog/spdlog.h"

namespace qbe::axis {
using namespace std;

Axis::Axis() {
  // // // qDebug("[axis] width: %f, height: %f, unit: %f, precision: %f", width, height, unit, precision);
  width = 1024;
  height = 768;
  unit = 1e-6;
  precision = 1e-9;
  scale = 1;
  unit_scale = unit / precision;
  // cell 的系数，由unit / precision;
  cell_scale = 1;
  // 缩放系数;
  scale = 1;  // 1 对应 mm;
  scale_max = 40000;
  scale_min = 0.002;
  // 线宽;
  line_width = 1;
  // 坐标轴偏移量;
  dx = 0;
  dy = 0;
  // matrix: [scaleX, 0, tranX, 0, scaleY, tranY, 0, 0, 1];
  // matrix = [scale, 0, dx, 0, -scale, dy, 0, 0, 1];
  // matrix = TranMatrix3x3(scale, scale, dx, dy);
  // 单位转换后的真实坐标;
  real_x = 0;
  real_y = 0;
  // 鼠标移动坐标;
  pos_x = 0;
  pos_y = 0;
  pos_real_x = 0;
  pos_real_y = 0;
  mouse_pixel_x = 0;
  mouse_pixel_y = 0;
  zero_pixel_x = 0;
  zero_pixel_y = 0;
  // 绘图坐标;
  draw_x = 0;
  draw_y = 0;

  // 吸附;
  adsorp = true;
  Point adsorp_point(0, 0);
  adsorp_precision = 1;

  // 网格;
  AxisGrid grid;

  // 标尺X;
  AxisRuler rulerX(width, 20);
  AxisRuler rulerY(20, height);
  BBox real_box(0, 0, 0, 0);
}

Axis::~Axis() {}
void Axis::init(int width_, int height_, double unit_, double precision_) {
  width = width_;
  height = height_;
  unit = unit_;
  precision = precision_;
}

void Axis::setCenter() {
  dx = width / 2;
  dy = height / 2;
  scale = 1;
}

Point Axis::getDiagonalAdsorpPoint(Point& startPoint, Point& endPoint) {
  double dx = endPoint.x - startPoint.x;
  double dy = endPoint.y - startPoint.y;

  // 计算斜率和角度
  double angle = std::atan2(dy, dx) * 180 / M_PI;
  // 找到最接近的 45°倍数的角度
  int snappedAngle = static_cast<int>(std::round(angle / 45)) * 45;
  // 计算距离
  double distance = std::sqrt(dx * dx + dy * dy);

  // 根据最接近的 45°倍数的角度和距离计算新的终点
  double radians = snappedAngle * M_PI / 180;

  auto x = startPoint.x + static_cast<double>(distance * std::cos(radians));
  auto y = startPoint.y + static_cast<double>(distance * std::sin(radians));
  return Point{x, y};
};

/**
 * 计算网格吸附点
 * @return:number[]
 * @memberof QedaAxis
 */
Point Axis::getGridAdsorpPoint(double x, double y) {
  // 网格精度吸附点
  double p = 1000 * adsorp_precision / unit_scale;
  double x_min = std::floor(x / p);
  double x_max = std::ceil(x / p);
  double y_min = std::floor(y / p);
  double y_max = std::ceil(y / p);

  Point point(x, y);
  Point p_left_down(x_min * p, y_min * p);
  Point p_left_top(x_min * p, y_max * p);
  Point p_rigth_down(x_max * p, y_min * p);
  Point p_rigth_top(x_max * p, y_max * p);

  double d_left_down = point.distance(p_left_down);
  double d_left_top = point.distance(p_left_top);
  double d_rigth_down = point.distance(p_rigth_down);
  double d_rigth_top = point.distance(p_rigth_top);
  // min_d = min(d_left_down, d_left_top, d_rigth_down, d_rigth_top);
  double min_d = min(d_left_down, d_left_top);
  min_d = min(d_rigth_down, min_d);
  min_d = min(d_rigth_top, min_d);

  if (d_left_down == min_d)
    return p_left_down;
  if (d_left_top == min_d)
    return p_left_top;
  if (d_rigth_down == min_d)
    return p_rigth_down;
  if (d_rigth_top == min_d)
    return p_rigth_top;
  return p_left_down;
}

/**
 * 生成网格线
 * @param:
 * @return:Lines
 * @memberof QedaAxis
 */
void Axis::getGridLine() {
  if (grid.show) {
    // 计算x、y最大，最小
    double s_x_min = bounds.x_min / scale;
    double s_x_max = bounds.x_max / scale;
    double s_y_min = bounds.y_min / scale;
    double s_y_max = bounds.y_max / scale;
    grid.lines.clear();
    for (int i = grid.x_left; i < grid.x_right; i++) {
      std::vector<Point> t = {};
      t.push_back(Point(i * grid.pixel_width, s_y_min));
      t.push_back(Point(i * grid.pixel_width, s_y_max));
      grid.lines.push_back(t);
    }
    for (int i = grid.y_left; i < grid.y_right; i++) {
      std::vector<Point> t = {};
      t.push_back(Point(s_x_min, i * grid.pixel_width));
      t.push_back(Point(s_x_max, i * grid.pixel_width));
      grid.lines.push_back(t);
    }
  }
}

// // 获取尺子X刻度和文字
void Axis::getRulerX() {
  rulerX.paths.clear();
  rulerX.texts.clear();

  double r_dx = dx;
  double r_dy = rulerX.height;
  int left = grid.x_left;
  int right = grid.x_right;
  double s1 = grid.pixel_width;

  // 判断单位刻度值是否过于大或过小，放置后面0个数太多，显示重合，需要减少数量
  // 获取线条和文字
  for (int i = left; i < right; i++) {
    double value = i * grid.real_width;
    if (i % 10 == 0) {
      QVector<double> p, t;
      p.append(r_dx + i * s1);      // x1
      p.append(r_dy);               // y1
      p.append(r_dx + i * s1);      // x2
      p.append(r_dy - 15);          // y2
      p.append(10);                 // length
      p.append(r_dx + i * s1 + 2);  // label x
      p.append(r_dy - 6);           // label y
      p.append(value);              // label
      rulerX.paths.append(p);
      // rulerX.texts.append(t);
    } else if (i % 5 == 0) {
      QVector<double> p, t;
      p.append(r_dx + i * s1);  // x1
      p.append(r_dy);           // y1
      p.append(r_dx + i * s1);  // x2
      p.append(r_dy - 10);      // y2
      p.append(5);              // length
      p.append(r_dx + i * s1 + 2);
      p.append(r_dy - 6);
      p.append(value);
      rulerX.paths.append(p);
      // rulerX.texts.append(t);
    } else {
      QVector<double> p, t;
      p.append(r_dx + i * s1);  // x1
      p.append(r_dy);           // y1
      p.append(r_dx + i * s1);  // x2
      p.append(r_dy - 5);       // y2
      p.append(3);              // length
      rulerX.paths.append(p);
    }
  }
}

// // 获取尺子Y刻度和文字
void Axis::getRulerY() {
  rulerY.paths.clear();
  rulerY.texts.clear();

  double r_dx = rulerY.width;
  double r_dy = dy;
  int left = grid.y_left;
  int right = grid.y_right;
  double s1 = grid.pixel_width;

  for (int i = left; i < right; i++) {
    double value = i * grid.real_width;
    if (i % 10 == 0) {
      QVector<double> p, t;
      p.append(r_dx);           // y1
      p.append(r_dy - i * s1);  // x1
      p.append(r_dx - 15);      // y2
      p.append(r_dy - i * s1);  // x2
      p.append(10);             // length
      p.append(r_dx - 6);
      p.append(r_dy - i * s1);
      p.append(value);
      rulerY.paths.append(p);
      // rulerY.texts.append(t);
    } else if (i % 5 == 0) {
      QVector<double> p, t;
      p.append(r_dx);           // y1
      p.append(r_dy - i * s1);  // x1
      p.append(r_dx - 10);      // y2
      p.append(r_dy - i * s1);  // x2
      p.append(5);              // length
      p.append(r_dx - 6);       // y2
      p.append(r_dy - i * s1);  // x2
      p.append(value);
      rulerY.paths.append(p);
      // rulerY.texts.append(t);
    } else {
      QVector<double> p, t;
      p.append(r_dx);           // y1
      p.append(r_dy - i * s1);  // x1
      p.append(r_dx - 5);       // y2
      p.append(r_dy - i * s1);  // x2
      p.append(3);              // length
      rulerY.paths.append(p);
    }
  }
}

// 网格切割真实包围盒子，用于四叉树分区块获取
void Axis::getSplitsAABB() {
  bounds_box_splits = {};
  // 避开区域
  auto bbox = bounds_box;
  double sw = bbox.width() / splits_col;
  double sh = bbox.height() / splits_row;
  for (int i = 0; i < splits_num; i++) {
    int cn = int(i % splits_col);  // 列号
    int rn = int(i / splits_col);  // 行号
    double dx = +cn * sw;
    double dy = -rn * sh;
    BBox box{
        bbox.x_min + dx,
        bbox.y_max - sh + dy,
        bbox.x_min + dx + sw,
        bbox.y_max - sh + dy + sh,
    };
    bounds_box_splits.push_back(box);
  }
}

// 设置视图区域, 像素坐标
void Axis::setViewBox(double x, double y, double w, double h) {
  auto cx = x + w / 2;
  auto cy = y + h / 2;
  auto scale_w = width / w;
  auto scale_h = height / h;
  scale = scale_w > scale_h ? scale_h : scale_w;

  dx = width / 2 - cx * scale;
  dy = height / 2 + cy * scale;
  onMouseMove(0, 0);
}

void Axis::backZero() {
  dx = width / 2;
  dy = height / 2;
  onMouseMove(0, 0);
}

// 更新坐标信息
void Axis::updateValue() {
  unit_scale = unit / (precision * cell_scale);

  // 像素坐标, 转换为笛卡尔坐标系
  pos_real_x = pos_x - dx;
  pos_real_y = -(pos_y - dy);

  // 转换系数, 像素转真实坐标系数
  double factor = 1000.0 / scale / unit_scale;

  // 真实坐标，单位转换;
  real_x = pos_real_x * factor;
  real_y = pos_real_y * factor;

  //
  board_real_x = real_x;
  board_real_y = real_y;
  // 固定坐标精度;
  if (unit == 1.0e-2) {
    real_x = round(real_x * 10e8) / 10e8;
    real_y = round(real_y * 10e8) / 10e8;
  }
  if (unit == 1.0e-3) {
    real_x = round(real_x * 10e7) / 10e7;
    real_y = round(real_y * 10e7) / 10e7;
  }
  if (unit == 1.0e-6) {
    real_x = round(real_x * 10e4) / 10e4;
    real_y = round(real_y * 10e4) / 10e4;
  }
  if (unit == 1.0e-9) {
    real_x = round(real_x * 10) / 10;
    real_y = round(real_y * 10) / 10;
  }

  // 绘图坐标;
  draw_x = real_x / unit;
  draw_y = real_y / unit;

  // 线宽;
  line_width = 1 / scale;

  // 计算边框;
  bounds.x_min = -dx;
  bounds.y_min = -(height - dy);
  bounds.x_max = width - dx;
  bounds.y_max = dy;

  // 真实边框
  bounds_box.x_min = bounds.x_min / scale;
  bounds_box.y_min = bounds.y_min / scale;
  bounds_box.x_max = bounds.x_max / scale;
  bounds_box.y_max = bounds.y_max / scale;

  // 单位边框
  double s_unit = 1 / scale / unit_scale;
  bounds_unit.x_min = bounds.x_min * s_unit;
  bounds_unit.y_min = bounds.y_min * s_unit;
  bounds_unit.x_max = bounds.x_max * s_unit;
  bounds_unit.y_max = bounds.y_max * s_unit;

  // 鼠标像素坐标
  mouse_pixel_x = pos_x;
  mouse_pixel_y = pos_y;

  // 原点像素坐标
  zero_pixel_x = dx;
  zero_pixel_y = dy;

  // 吸附
  Point ab_point = getGridAdsorpPoint(real_x, real_y);
  // 吸附点真实坐标
  adsorp_point.x = ab_point.x;
  adsorp_point.y = ab_point.y;
  // 吸附点真实坐标
  adsorp_real_point.x = ab_point.x;
  adsorp_real_point.y = ab_point.y;
  // 吸附像素坐标
  adsorp_pixel_point.x = ab_point.x / factor + dx;
  adsorp_pixel_point.y = -(ab_point.y / factor) + dy;

  // 转换矩阵
  matrix.setScale(scale, scale);
  matrix.setTranslate(dx, dy);
  Point mouse_point(round(real_x * unit_scale), round(real_y * unit_scale));

  // 鼠标盒子
  // bounds_mouse.x_min = mouse_point.x - bounds_mouse_range / scale;
  // bounds_mouse.y_min = mouse_point.y - bounds_mouse_range / scale;
  // bounds_mouse.x_max = mouse_point.x + bounds_mouse_range / scale;
  // bounds_mouse.x_max = mouse_point.y + bounds_mouse_range / scale;
  bounds_mouse.x_min = adsorp_real_point.x - bounds_mouse_range / scale;
  bounds_mouse.y_min = adsorp_real_point.y - bounds_mouse_range / scale;
  bounds_mouse.x_max = adsorp_real_point.x + bounds_mouse_range / scale;
  bounds_mouse.y_max = adsorp_real_point.y + bounds_mouse_range / scale;
  // auto box = bounds_mouse;
  // SPDLOG_INFO("bounds_mouse: [{:.2f},{:.2f}], [{:.2f},{:.2f}]", box.x_min, box.y_min, box.x_max, box.y_max);

  // 网格生成, 通过画板真实宽度，获取对应范围内的精度
  double real_width = width * factor / 2;  // 除以2，显示更密集
  double pre_k = 0;
  for (const auto& it : grid.real_width_map) {
    double left = pre_k;
    double right = it.first;
    if (real_width > left && real_width <= right) {
      grid.real_width = it.second;
      break;
    }
    pre_k = it.first;
  }
  grid.pixel_width = grid.real_width / factor;
  grid.x_left = ceil((bounds.x_min / grid.pixel_width));
  grid.x_right = ceil((bounds.x_max / grid.pixel_width));
  grid.y_left = ceil((bounds.y_min / grid.pixel_width));
  grid.y_right = ceil((bounds.y_max / grid.pixel_width));
  rulerX.w = grid.pixel_width;
  rulerY.w = grid.pixel_width;
  // SPDLOG_INFO("[real width] real_width: {:.2f}", real_width);
  // SPDLOG_INFO("zero_px: {:.2f}, zero_py: {:.2f}", zero_pixel_x, zero_pixel_y);

  // opengl 坐标系统相关变量
  gl_dx = -width / 2.0 + dx;
  gl_dy = -(-height / 2.0 + dy);
  gl_zero_px = gl_dx;
  gl_zero_py = gl_dy;
  gl_mouse_px = -width / 2.0 + pos_x;
  gl_mouse_py = -(-height / 2.0 + pos_y);
  gl_real_px = pos_real_x;
  gl_real_py = pos_real_y;
  gl_adsorp_px = -width / 2.0 + adsorp_pixel_point.x;
  gl_adsorp_py = -(-height / 2.0 + adsorp_pixel_point.y);

  // getGridLine();
  getRulerX();
  getRulerY();
  getSplitsAABB();
}

void Axis::tranlate(float _dx, float _dy) {
  dx += _dx;
  dy -= _dy;
  updateValue();
}
// // 鼠标移动事件
// void Axis::onMouseMove(QMouseEvent *e)
// {
//     static int x = 0, y = 0;
//     if(x != 0 && y !=0 )
//     {
//         dx = dx + (x - e->localPos().x());
//         dy = dy + (y - e->localPos().y());
//     }
//     x = e->localPos().x();
//     y = e->localPos().y();
// }
void Axis::onMouseMove(float x, float y) {
  // pos_x = e->x();
  // pos_y = e->y();
  pos_x = x;
  pos_y = y;
  updateValue();
}

void Axis::onMouseWheel(int delta) {
  float s = 2;
  if (delta < 0) {
    // qDebug("wheel up");
    if (scale >= scale_min * s) {
      dx = dx + (pos_x - dx) / s;
      dy = dy + (pos_y - dy) / s;
      scale = scale / s;
    }
  } else {
    // qDebug("wheel down");
    if (scale <= scale_max / s) {
      dx = dx - (pos_x - dx);
      dy = dy - (pos_y - dy);
      scale = scale * s;
    }
  }
  // scale = scale <= 0.002 ? 0.0019987654312312 : scale;
  // scale = scale >= 40000 ? 40000 : scale;
  updateValue();
}

void Axis::onMouseWheel(QWheelEvent* e) {
  float s = 2;
  if (e->angleDelta().y() < 0) {
    // qDebug("wheel up");
    if (scale >= scale_min * s) {
      dx = dx + (pos_x - dx) / s;
      dy = dy + (pos_y - dy) / s;
      scale = scale / s;
    }
  } else {
    // qDebug("wheel down");
    if (scale <= scale_max / s) {
      dx = dx - (pos_x - dx);
      dy = dy - (pos_y - dy);
      scale = scale * s;
    }
  }
  scale = scale <= 0.002 ? 0.0019987654312312 : scale;
  scale = scale >= 40000 ? 40000 : scale;

  updateValue();
}

std::vector<double> Axis::getScreenBox() {
  double offset_x = (dx - width / 2) / scale;
  double offset_y = (dy - height / 2) / scale;
  std::vector<double> res;
  res.push_back(-(width / 2) / scale - offset_x);
  res.push_back(-(height / 2) / scale + offset_y);
  res.push_back(width / 2 / scale - offset_x);
  res.push_back(height / 2 / scale + offset_y);
  return res;
}

void Axis::adaptBox(double min_x, double min_y, double max_x, double max_y) {
  if (max_x <= min_x || max_y <= min_y) {
    min_x = -0.5;
    min_y = -0.5;
    max_x = 0.5;
    max_y = 0.5;
  }
  auto center_x = (max_x + min_x) / 2;
  auto center_y = (max_y + min_y) / 2;
  double _scale = 0;
  double scale_w = width / (max_x - min_x);
  double scale_h = height / (max_y - min_y);
  _scale = scale_w;
  if (_scale > scale_h)
    _scale = scale_h;
  _scale = _scale * 0.9;

  _scale = _scale <= 0.002 ? 0.0019987654312312 : _scale;
  _scale = _scale >= 40000 ? 40000 : _scale;

  scale = _scale;
  dx = width / 2 - center_x * scale;
  dy = height / 2 + center_y * scale;
  updateValue();
}

}  // namespace qbe::axis